﻿#include <graphics.h>  
#include <conio.h>
#include <vector>
#include <time.h>
#include "EasyXPng.h"
#include "Timer.h"
#pragma comment(lib,"Winmm.lib")

using namespace std;

#define  WIDTH 800  
#define  HEIGHT 600

void startup();
void show();
void update_without_input();
void update_with_input();
int rand_between_min_max(int min, int max);
void play_music_once(TCHAR fileName[80]);

enum player_status // 枚举类型，游戏角色所有的可能状态
{
	stand_left, stand_right, run_left, run_right, jump_left, jump_right, die
};


class Land
{
public:
	IMAGE im_land;
	float left_x;
	float right_x;
	float top_y;
	float land_width;
	float land_height;

	void initialize()
	{
		loadimage(&im_land, _T("land.png"));
		land_width = im_land.getwidth();
		land_height = im_land.getheight();
		left_x = WIDTH / 2;
		right_x = left_x + land_width;
		top_y = HEIGHT / 2;
	}

	void draw(float px, float py)
	{
		putimage(left_x - px, top_y - py, &im_land);
	}
};

class Enemy
{
public:
	IMAGE im_enemy;
	float x;
	float y;
	float enemy_width;
	float enemy_height;
	float x_min;
	float x_max;
	float speed_x;

	void initialize()
	{
		loadimage(&im_enemy, _T("bat.png"));
		enemy_width = im_enemy.getwidth();
		enemy_height = im_enemy.getheight();
	}

	void draw(float px, float py)
	{
		putimagePng(x - enemy_width / 2 - px, y - enemy_height / 2 - py, &im_enemy);
	}

	void update()
	{
		x += speed_x;
		if (x > x_max || x < x_min)
			speed_x = -speed_x;
	}
};

class Scene
{
public:
	IMAGE im_bk;
	IMAGE im_star;
	vector<Land> lands;
	vector<Enemy> enemies;
	int level;
	int last_level;

	void draw(float px, float py)
	{
		putimage(-px / 20, -100 - py / 20, &im_bk);

		for (int i = 0; i < lands.size(); i++)
		{
			lands[i].draw(px, py);
		}

		for (int i = 0; i < enemies.size(); i++)
		{
			enemies[i].draw(px, py);
		}

		putimagePng(lands[lands.size() - 1].left_x + im_star.getwidth() - px, lands[lands.size() - 1].top_y - im_star.getheight() - py, &im_star);

		TCHAR text[20];
		setbkmode(TRANSPARENT);
		swprintf_s(text, _T("第 %d 关"), level);
		settextcolor(RGB(0, 50, 200));
		settextstyle(30, 0, _T("楷体"));
		outtextxy(WIDTH * 0.45, 30, text);
	}

	void initialize()
	{
		TCHAR file_name[80];
		int i = level % 9 + 1;
		swprintf_s(file_name, _T("landscape%d.png"), i);
		loadimage(&im_bk, file_name);
		loadimage(&im_star, _T("star.png"));

		if (lands.size() == 0)
		{
			level = 1;
			last_level = 1;
		}
		if (lands.size() == 0 || level > last_level)
		{
			lands.clear();

			Land land1;
			land1.initialize();
			lands.push_back(land1);

			for (int i = 1; i < 10 + level * 2; i++)
			{
				Land land2;
				land2.initialize();
				int r1 = rand_between_min_max(1, 30);
				if (r1 > level)
				{
					land2.left_x = land1.left_x + land2.land_width;
				}
				else
				{
					land2.left_x = land1.left_x + 2 * land2.land_width;
				}

				int r3 = rand_between_min_max(1, 20);
				if (r1 > level)
					land2.top_y = land1.top_y;
				else
				{
					int r3 = rand_between_min_max(-1, 1);
					land2.top_y = WIDTH / 2 + HEIGHT / 10 * r3;
				}

				land2.right_x = land2.left_x + land2.land_width;

				lands.push_back(land2);
				land1 = land2;
			}

			enemies.clear();

			int numEnemy = (level + 3) / 5;
			int idStep = lands.size() / (numEnemy + 1);
			for (int j = 1; j <= numEnemy; j++)
			{
				Enemy enemy;
				enemy.initialize();
				int landId = j * idStep;
				enemy.x = lands[landId].left_x + lands[landId].land_width / 2;
				enemy.y = lands[landId].top_y - enemy.enemy_height;

				float movingRange = enemy.enemy_width * (3 + level / 15.0);
				enemy.x_min = enemy.x - movingRange;
				enemy.x_max = enemy.x + movingRange;
				enemy.speed_x = 2 + level / 10.0;

				enemies.push_back(enemy);
			}
		}
	}
};

class Player
{
public:
	IMAGE im_show;
	IMAGE im_stand_right;
	IMAGE im_stand_left;
	IMAGE im_jump_right;
	IMAGE im_jump_left;
	vector <IMAGE> ims_run_right;
	vector <IMAGE> ims_run_left;
	int anim_Id;
	player_status player_status;
	float x_left;
	float y_bottom;
	float speed_x;
	float speed_y;
	float gravity;
	float width;
	float height;

	void draw()
	{
		putimagePng(WIDTH / 2, HEIGHT / 2 - height, &im_show);
	}


	void initialize()
	{
		ims_run_left.clear();
		ims_run_right.clear();

		loadimage(&im_stand_right, _T("standright.png"));
		loadimage(&im_stand_left, _T("standleft.png"));
		loadimage(&im_jump_right, _T("jumpright.png"));
		loadimage(&im_jump_left, _T("jumpleft.png"));

		player_status = stand_right;
		im_show = im_stand_right;
		width = im_stand_right.getwidth();
		height = im_stand_right.getheight();

		TCHAR filename[80];
		for (int i = 0; i <= 7; i++)
		{
			swprintf_s(filename, _T("runright%d.png"), i);
			IMAGE im;
			loadimage(&im, filename);
			ims_run_right.push_back(im);
		}
		for (int i = 0; i <= 7; i++)
		{
			swprintf_s(filename, _T("runleft%d.png"), i);
			IMAGE im;
			loadimage(&im, filename);
			ims_run_left.push_back(im);
		}

		anim_Id = 0;

		update_xy(WIDTH / 2, HEIGHT / 2);

		speed_x = 10;
		speed_y = 0;
		gravity = 3;

	}


	void update_xy(float mx, float my)
	{
		x_left = mx;
		y_bottom = my;
	}


	void runright(Scene& scene)
	{
		x_left += speed_x;

		if (is_not_on_all_lands(scene.lands, speed_y))  //移动后不在任何一块地面上了则切换到向右起跳状态。
		{
			im_show = im_jump_right;
			player_status = jump_right;
			return;
		}

		if (player_status == jump_left || player_status == jump_right) // 如果是起跳状态则改变造型为向右起跳造型。
		{
			im_show = im_jump_right;
		}
		else
		{
			if (player_status != run_right) //如果之前角色状态不是向右奔跑则切换为向右奔跑状态。
			{
				player_status = run_right;
				anim_Id = 0;
			}
			else
			{
				anim_Id++;
				if (anim_Id >= ims_run_right.size())
				{
					anim_Id = 0;
				}
			}
			im_show = ims_run_right[anim_Id];
		}
	}


	void runleft(Scene& scene)
	{
		x_left -= speed_x;

		if (is_not_on_all_lands(scene.lands, speed_y))  //移动后不在任何一块地面上了则切换到向左起跳状态。
		{
			im_show = im_jump_left;
			player_status = jump_left;
			return;
		}

		if (player_status == jump_left || player_status == jump_right) //如果是起跳状态则改变造型为向左起跳造型。
		{
			im_show = im_jump_left;
		}
		else
		{
			if (player_status != run_left) //如果之前角色状态不是向左奔跑则切换为向左奔跑状态。
			{
				player_status = run_left;
				anim_Id = 0;
			}
			else
			{
				anim_Id++;
				if (anim_Id >= ims_run_left.size())
				{
					anim_Id = 0;
				}
			}
			im_show = ims_run_left[anim_Id];
		}
	}


	void stand_still()
	{
		if (player_status == stand_left)
		{
			im_show = im_stand_left;
		}
		else if (player_status == stand_right)
		{
			im_show = im_stand_right;
		}
	}


	void begin_jump() //按下w或向上方向键后游戏角色跳跃。
	{
		if (player_status != jump_left && player_status != jump_right) //如已经在空中则不起跳。
		{
			if (player_status == run_left || player_status == stand_left)  //起跳前是向左跑或向左站立状态则切换到向左起跳状态。
			{
				im_show = im_jump_left;
				player_status = jump_left;
			}
			else if (player_status == run_right || player_status == stand_right)//起跳前是向右跑或向右站立状态则切换到向右起跳状态。
			{
				im_show = im_jump_right;
				player_status = jump_right;
			}
			speed_y = -30;
		}
	}


	int is_collide_enemy(Enemy& enemy)
	{
		float x_center = x_left + width / 2;
		float y_center = y_bottom - height / 2;
		if (abs(enemy.x - x_center) <= enemy.enemy_width * 0.5 && abs(enemy.y - y_center) <= enemy.enemy_height * 0.5)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}


	int is_on_land(Land& land, float y_speed)
	{
		float x_right = x_left + width;
		if (y_speed <= 0) //y轴方向速度小于0，表示正在向上运动，不需要考虑速度的影响。
		{
			y_speed = 0;
		}
		if (land.left_x - x_left <= width * 0.6 && x_right - land.right_x <= width * 0.6 && abs(y_bottom - land.top_y) <= 5 + y_speed)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}

	int is_not_on_all_lands(vector<Land>& lands, float speed) //判断玩家是否不在所有的地面上。
	{
		for (int i = 0; i < lands.size(); i++)
		{
			if (is_on_land(lands[i], speed))
			{
				return 0; //在地面上则返回0。
			}
		}
		return 1; //不在所有地面上则返回1。
	}


	void update_y_coordinate(Scene& scene) //x坐标是由键盘控制的，而y坐标是每帧自动更新的。
	{
		if (player_status == jump_left || player_status == jump_right)
		{
			speed_y += gravity;
			y_bottom += speed_y;

			for (int i = 0; i < scene.lands.size(); i++)
			{
				if (is_on_land(scene.lands[i], speed_y))
				{
					y_bottom = scene.lands[i].top_y;
					if (player_status == jump_left) //如果向左跳，则落地后切换到向左站立方向。
					{
						player_status = stand_left;
					}
					if (player_status == jump_right) //如果向右跳，则落地后切换到向右站立方向。
					{
						player_status = stand_right;
					}
					break;
				}
			}
		}
	}
};

Player player;
Scene scene;
Timer timer;

int main()
{
	startup();
	while (!(_kbhit() && _getch() == 'q'))
	{
		show();
		update_without_input();
		update_with_input();
	}
	return 0;
}


void startup()
{
	mciSendString(_T("open game_music.mp3 alias bkmusic"), NULL, 0, NULL);//打开背景音乐
	mciSendString(_T("play bkmusic repeat"), NULL, 0, NULL);

	srand(time(0));

	scene.initialize();
	player.initialize();

	initgraph(WIDTH, HEIGHT);

	BeginBatchDraw();
}


void show()
{
	scene.draw(player.x_left - WIDTH / 2, player.y_bottom - HEIGHT / 2);
	player.draw();

	FlushBatchDraw();

	timer.Sleep(50);
}


void update_without_input()
{
	player.update_y_coordinate(scene);

	int land_size = scene.lands.size();
	//角色跑到最后一个地面上了则游戏胜利。
	if (player.x_left > scene.lands[land_size - 1].left_x && abs(player.y_bottom - scene.lands[land_size - 1].top_y) <= 2)
	{
		play_music_once(_T("success.mp3"));
		scene.last_level = scene.level;
		scene.level++;
		scene.initialize();
		player.initialize();
	}
	else if (player.y_bottom > 1.5 * HEIGHT)  //角色落到底面则游戏失败，重新开始。
	{
		play_music_once(_T("warning.mp3"));
		scene.last_level = scene.level;
		scene.initialize();
		player.initialize();
	}

	for (int i = 0; i < scene.enemies.size(); i++)
	{
		scene.enemies[i].update(); //所有敌人自动更新位置。
		//如果敌人和玩家碰撞则游戏失败。
		if (player.is_collide_enemy(scene.enemies[i]))
		{
			play_music_once(_T("warning.mp3"));
			scene.last_level = scene.level;
			scene.initialize();
			player.initialize();
		}
	}
}


void update_with_input()
{
	player.stand_still();

	if (_kbhit())
	{
		if (GetAsyncKeyState(VK_RIGHT) || GetAsyncKeyState('D'))
		{
			player.runright(scene);
		}
		else if (GetAsyncKeyState(VK_LEFT) || GetAsyncKeyState('A'))
		{
			player.runleft(scene);
		}
		if (GetAsyncKeyState(VK_UP) || GetAsyncKeyState('W'))
		{
			player.begin_jump();
		}
	}
}

int rand_between_min_max(int min, int max)
{
	int r = rand() % (max - min + 1) + min;
	return r;
}


void play_music_once(TCHAR fileName[80])
{
	TCHAR cmdString1[50];
	swprintf_s(cmdString1, _T("open %s alias tmpmusic"), fileName);
	mciSendString(_T("close tmpmusic"), NULL, 0, NULL);
	mciSendString(cmdString1, NULL, 0, NULL);
	mciSendString(_T("play tmpmusic"), NULL, 0, NULL);
}